2.7 自定义类型
使用关键字type定义用户自定义类型,包括基于现有基础类型创建,或者是结构体、函数类型等。
type flags byte
const(
read flags=1<<iota
write
exec
)
func main() {
f:=read|exec
fmt.Printf("%b\n",f) // 输出二进制标记位
}
输出:
101
和var、const类似,多个type定义可合并成组,可在函数或代码块内定义局部类型。
func main() {
type( // 组
user struct{ // 结构体
name string
age uint8
}
event func(string)bool // 函数类型
)
u:=user{"Tom",20}
fmt.Println(u)
var f event=func(s string)bool{
println(s)
return s!= ""
}
f("abc")
}即便指定了基础类型,也只表明它们有相同底层数据结构,两者间不存在任何关系,属完全不同的两种类型。除操作符外,自定义类型不会继承基础类型的其他信息(包括方法)。不能视作别名,不能隐式转换,不能直接用于比较表达式。
func main() {
type data int
var d data=10
var x int=d // 错误:cannot use d(type data)as type int in assignment
println(x)
println(d==x) // 错误:invalid operation:d==x(mismatched types data and int)
}
未命名类型
与有明确标识符的bool、int、string等类型相比,数组、切片、字典、通道等类型与具体元素类型或长度等属性有关,故称作未命名类型(unnamed type)。当然,可用type为其提供具体名称,将其改变为命名类型(named type)。
具有相同声明的未命名类型被视作同一类型。
- 具有相同基类型的指针。
- 具有相同元素类型和长度的数组(array)。
- 具有相同元素类型的切片(slice)。
- 具有相同键值类型的字典(map)。
- 具有相同数据类型及操作方向的通道(channel)。
- 具有相同字段序列(字段名、字段类型、标签,以及字段顺序)的结构体(struct)。
- 具有相同签名(参数和返回值列表,不包括参数名)的函数(func)。
- 具有相同方法集(方法名、方法签名,不包括顺序)的接口(interface)。
相关类型会在后续章节做详细说明,此处无须了解更多细节。
容易被忽视的是struct tag,它也属于类型组成部分,而不仅仅是元数据描述。
func main() {
var a struct{ // 匿名结构类型
x int `x`
s string `s`
}
var b struct{
x int
s string
}
b=a // 错误:cannot use a type
// struct{x int"x";s string"s" }as type
// struct{x int;s string}in assignment
fmt.Println(b)
}同样,函数的参数顺序也属签名组成部分。
func main() {
var a func(int,string)
var b func(string,int)
b=a // 错误:cannot use a(type func(int,string))as type
// func(string,int)in assignment
b("s",1)
}未命名类型转换规则:
- 所属类型相同。
- 基础类型相同,且其中一个是未命名类型。
- 数据类型相同,将双向通道赋值给单向通道,且其中一个为未命名类型。
- 将默认值nil赋值给切片、字典、通道、指针、函数或接口。
- 对象实现了目标接口。
func main() {
type data[2]int
var d data= [2]int{1,2} // 基础类型相同,右值为未命名类型
fmt.Println(d)
a:=make(chan int,2)
var b chan<-int=a // 双向通道转换为单向通道,其中b为未命名类型
b<-2
}